package com.miao.im.support;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.miao.im.common.CacheKey;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * @Auther: miaoguoxin
 * @Date: 2021/5/12 14:36
 * @Description: 消息序号管理器
 */
@Component
@Slf4j
public class IdManager {
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Value("${server.port}")
    private Integer port;

    private long workerId;

    private long dataCenterId;

    @PostConstruct
    public void init() throws UnknownHostException {
        InetAddress addr = InetAddress.getLocalHost();
        //优先递增使用dataCenterId
        this.setDataCenterId(addr, false);
    }


    /**
     * 生成一个趋势递增id
     * @author miaoguoxin
     * @date 2021/5/12
     */
    public long generateId() {
        return IdUtil.getSnowflake(workerId, dataCenterId).nextId();
    }


    private void setAndIncWorkerId(InetAddress addr,long inc) {
        String workerIdKey = CacheKey.SNOWFLAKE_WORKER_ID_PREFIX + addr + ":" + port;
        String workerId = redisTemplate.opsForValue().get(workerIdKey);
        if (StrUtil.isBlank(workerId)) {
            Long curWorkerId = redisTemplate.opsForValue().increment(CacheKey.SNOWFLAKE_CUR_WORKER_ID,inc);
            if (curWorkerId <= 31) {
                redisTemplate.opsForValue().set(workerIdKey, curWorkerId + "");
                this.workerId = curWorkerId;
            } else {
                this.setDataCenterId(addr, true);
            }
        } else {
            this.workerId = Long.parseLong(workerId);
        }
    }

    private void setDataCenterId(InetAddress addr, boolean fromWorkerId) {
        String dataCenterIdKey = CacheKey.SNOWFLAKE_DATACENTER_ID_PREFIX + addr + ":" + port;
        String dataCenterId = redisTemplate.opsForValue().get(dataCenterIdKey);
        if (StrUtil.isBlank(dataCenterId)) {
            Long curDataCenterId = redisTemplate.opsForValue().increment(CacheKey.SNOWFLAKE_CUR_DATACENTER_ID);
            if (curDataCenterId <= 31) {
                redisTemplate.opsForValue().set(dataCenterIdKey, curDataCenterId + "");
                this.dataCenterId = curDataCenterId;
                this.setAndIncWorkerId(addr,0);
            } else {
                if (!fromWorkerId) {
                    this.setAndIncWorkerId(addr,1);
                } else {
                    throw new IllegalArgumentException("workerId和dataCenterId已达到最大限度");
                }
            }
        } else {
            this.dataCenterId = Long.parseLong(dataCenterId);
            this.setAndIncWorkerId(addr,0);
        }
    }
}
